package gl.vbm

import org.lwjgl.opengl.GL30C.*
import java.nio.ByteBuffer

class VBMObject(buffer: ByteBuffer) {

    val header: VBMHeader
    val attribs: Array<VBMAttribute>
    val frames: Array<VBMFrameHeader>
    val materials: Array<VBMMaterial>

    val positionData: FloatArray
    val positionSize: Long
    val normalData: FloatArray
    val normalSize: Long
    val tex0Data: FloatArray
    val tex0Size: Long
    val indexData: Any // ShortArray or IntArray, depends on header.indexType

    init {
        // header
        header = VBMHeader(buffer)
        // attrib headers
        attribs = Array(header.attribCount) {
            return@Array VBMAttribute(buffer)
        }
        // frame headers
        frames = Array(header.attribCount) {
            return@Array VBMFrameHeader(buffer)
        }
        // data
        positionData = FloatArray(attribs.size * attribs[0].components) { buffer.float }
        positionSize = positionData.size * 4L
        normalData = FloatArray(attribs.size * attribs[1].components) { buffer.float }
        normalSize = normalData.size * 4L
        tex0Data = FloatArray(attribs.size * attribs[2].components) { buffer.float }
        tex0Size = tex0Data.size * 4L
        indexData = when (header.indexType) {
            GL_UNSIGNED_SHORT -> ShortArray(header.indexCount) { buffer.short }
            GL_UNSIGNED_INT -> IntArray(header.indexCount) { buffer.int }
            else -> throw RuntimeException("Unknown index type ${header.indexType}, only can be GL_UNSIGNED_SHORT($GL_UNSIGNED_SHORT) or GL_UNSIGNED_INT($GL_UNSIGNED_INT)")
        }
        // material
        materials = Array(header.attribCount) {
            return@Array VBMMaterial(buffer)
        }
    }

    fun createVAO(vertex: Int = 0, normal: Int = 1, texCoord: Int = 2): VAOResult {
        val vao = glGenVertexArrays()
        val buffer = glGenBuffers()
        glBindBuffer(GL_ARRAY_BUFFER, buffer)
        glBufferData(GL_ARRAY_BUFFER, positionSize + normalSize + tex0Size, GL_STATIC_DRAW)
        val offVertex = 0L
        val offNormal = offVertex + normalSize
        val offTexCoord = offNormal + tex0Size
        glBufferSubData(GL_ARRAY_BUFFER, offVertex, positionData)
        glBufferSubData(GL_ARRAY_BUFFER, offNormal, normalData)
        glBufferSubData(GL_ARRAY_BUFFER, offTexCoord, tex0Data)
        glBindVertexArray(vao)
        glEnableVertexAttribArray(vertex)
        glVertexAttribPointer(vertex, attribs[0].components, attribs[0].type, false, 0, offVertex)
        glVertexAttribPointer(vertex, attribs[1].components, attribs[1].type, false, 0, offNormal)
        glVertexAttribPointer(vertex, attribs[2].components, attribs[2].type, false, 0, offTexCoord)
        return VAOResult(vao, buffer, vertex, normal, texCoord)
    }
}

